
/*! \file bmp085.c
    \brief This file contains all function implementations for the BMP085 API
    
    Details.
*/


/******************************************************************************
                   Includes section
******************************************************************************/
#include <bmp085.h>
#include <i2cPacket.h>
#include <appTimer.h>
#include <gpio.h>
#include <sensors.h>

/******************************************************************************
                   Define(s) section
******************************************************************************/
#define BMP085_DEVICE_ADDR	(0xEE >> 1)

#define BMP085_PROM_START_ADDR			0xAA
#define BMP085_PROM_DATA_LEN			22

#define BMP085_CTRL_MEAS_REGISTER		0xF4
#define BMP085_ADC_OUT_MSB_REGISTER		0xF6
#define BMP085_ADC_OUT_LSB_REGISTER		0xF7
#define BMP085_SOFT_RESET_REGISTER		0xE0

#define BMP085_T_MEASURE        0x2E				// temperature measurement
#define BMP085_P_MEASURE        0x34				// pressure measurement

/*
	Mode					Oversampling	# of Samples	Convertion Time [ms]
	ultra-low-power			0				1				4.5
	standard				1				2				7.5
	high resolution			2				4				13.5
	ultra-high-resolution	3				8				25.5
*/
#define BMP085_OVERSAMPLING					3	// oversampling setting
#define BMP085_DELAY						40	// in milliseconds

#define BMP085_STATE_QUEUE_LEN				20	// state queue length
#define BMP085_BUFFER_LENGHT				150	// buffer length for USART debug

/******************************************************************************
                   Types section
******************************************************************************/

// calibration structure
typedef struct {
	uint8_t prom_data[BMP085_PROM_DATA_LEN];
	uint8_t ut[2];
	uint8_t up[3];
    int16_t ac1;
    int16_t ac2;
    int16_t ac3;
    uint16_t ac4;
    uint16_t ac5;
    uint16_t ac6;
    int16_t b1;
    int16_t b2;
    int16_t mb;
    int16_t mc;
    int16_t md;	

	int32_t param_b5;
} Bmp085Calibration_t;


/******************************************************************************
                   Global variables section
******************************************************************************/
// The flow control variable
static Bmp085Control_t bmp085Control;

// hods a FIFO queue of task for the bspBMP085Handler task handler function
volatile Bmp085States_t bmp085State[BMP085_STATE_QUEUE_LEN] = { BMP085_IDLE };
// the contents of the PROM and the subsequent synthesized values
static Bmp085Calibration_t bmp085Calibration;

// I2C access control variable
static HAL_I2cDescriptor_t i2cParam = 
{
	.tty = TWI_CHANNEL_0,
	.id = BMP085_DEVICE_ADDR,
	.clockRate = I2C_CLOCK_RATE_62
};
// Timer structure, used to make the delays needed for A2D conversion
static HAL_AppTimer_t bmp085Delay;

// Auxiliary variables
uint8_t ctrl_reg_data;
int32_t x1,x2,x3,b3,b6;
uint32_t b4,b7,ut,up;

//static char msg[BMP085_BUFFER_LENGHT];

/******************************************************************************
                   Implementations section
******************************************************************************/

/**************************************************************************//**
\brief  Adds a new task to the task queue, FIFO style.

\param[in]
  task - the task to be queued.

\return
  BC_FAIL - if the queue if full, has more than BMP085_STATE_QUEUE_LEN tasks
  BC_SUCCESS - other case.

******************************************************************************/
result_t bmp085PushTask(Bmp085States_t task)
{	
	uint8_t i;
	for( i=0; i < BMP085_STATE_QUEUE_LEN-1; i++)
	{
		/* the queue is filled with IDLE tasks by default.
		The first IDLE task found will be replaced by the task we want to add. */
		if( bmp085State[i] == BMP085_IDLE )
		{
			bmp085State[i] = task;
			return BC_SUCCESS;
		}
	}
	return BC_FAIL;
}

/**************************************************************************//**
\brief	Returns the first task to be executed from the queue, FIFO style.

\return
	the task in queue to be executed.
******************************************************************************/
Bmp085States_t bmp085PopTask(void)
{	
	uint8_t i;
	Bmp085States_t task = bmp085State[0];
	
	for( i=0; i < BMP085_STATE_QUEUE_LEN-1; i++)
	{
		bmp085State[i] = bmp085State[i+1];
	}
	return task;
}


/**************************************************************************//**
\brief  Initializes the sensor and starts reads the calibration values.

\param[in]
  bmp085Result - pointer to a variable holding the result BC_SUCCESS/FAIL of
				 the whole operation.
\param[in]
  bmp085Pressure - pointer to a variable that will hold the pressure value.

\return
  BC_FAIL - if the sensor was open and running other tasks.
  BC_SUCCESS - other case.

******************************************************************************/
result_t openBmp085(void)
{
	if (BMP085_IDLE == bmp085State[0])
	{
		// exercise master clear
		GPIO_USART_DTR_make_out();
		GPIO_USART_DTR_clr();
		GPIO_USART_DTR_set();
		return BC_SUCCESS;
	}
	return BC_FAIL;
}


/**************************************************************************//**
\brief  Performs the test if the component have completed request.

\return
  BC_FAIL - the previous request is not completed.
  BC_SUCCESS - otherwise.
******************************************************************************/
result_t closeBmp085(void)
{
  if (BMP085_IDLE == bmp085State[0])
	GPIO_USART_DTR_clr();
    return BC_SUCCESS;

  return BC_FAIL;
}

/**************************************************************************//**
\brief Handles the I2C interrupts.

\param[in]
  result - the result of the operation true - successful, false - failed.
******************************************************************************/
void bmp085I2CHandler(bool result)
{
	// close resource
	HAL_CloseI2cPacket(&i2cParam);
	if (false == result)
	{
    	/* If failed, return BC_FAIL as result and call the upper task handler
	       with the return state in bmp085Control.bmp085Return assigned from
	       sensor.c file */
		if (bmp085Control.bmp085isPressureReading)
			bmp085Control.bmp085PressureCallback(false, (uint32_t)0x0000);
		else
			bmp085Control.bmp085TempCallback(false, (int16_t)0x0000);
	}
	else
	{
    	// If successful, return BC_SUCCESS and proceed to the next task
		bmp085Control.bmp085Result = true;
		bspPostTask(BSP_PRESSURE);
	}
}


/**************************************************************************//**
\brief Reads the calibration values from memory.
******************************************************************************/
result_t calibrateBmp085(void)
{
	if (BC_FAIL == bmp085PushTask(BMP085_GET_CAL))
		return BC_FAIL;
	if (BC_FAIL == bmp085PushTask(BMP085_WAIT))
		return BC_FAIL;
	if (BC_FAIL == bmp085PushTask(BMP085_FORMAT_CAL_DATA))
		return BC_FAIL;
	if (BC_FAIL == bmp085PushTask(BMP085_RETURN))
		return BC_FAIL;
	bspPostTask(BSP_PRESSURE);
	return BC_SUCCESS;
}

/**************************************************************************//**
\brief Reads the temperature value.
******************************************************************************/
result_t readBmp085Temperature(void (*f)(bool result, int16_t data))
{

	if (BMP085_IDLE != bmp085State[0])
		return BC_FAIL;

	if (!f)
		return BC_FAIL;

	bmp085Control.bmp085TempCallback = f;
	bmp085Control.bmp085isPressureReading = false;
	calibrateBmp085();
	if (BC_FAIL == bmp085PushTask(BMP085_SET_READ_TO_TEMP))
		return BC_FAIL;
	if (BC_FAIL == bmp085PushTask(BMP085_WAIT))
		return BC_FAIL;
	if (BC_FAIL == bmp085PushTask(BMP085_GET_UTEMP))
		return BC_FAIL;
	if (BC_FAIL == bmp085PushTask(BMP085_FORMAT_TEMP_DATA))
		return BC_FAIL;
	if (BC_FAIL == bmp085PushTask(BMP085_RETURN))
		return BC_FAIL;
	bspPostTask(BSP_PRESSURE);
	return BC_SUCCESS;
}

/**************************************************************************//**
\brief Reads the pressure value.
******************************************************************************/
result_t readBmp085Data(void (*f)(bool result, uint32_t data))
{
	if (BMP085_IDLE != bmp085State[0])
		return BC_FAIL;

	if (!f)
		return BC_FAIL;
		
	bmp085Control.bmp085PressureCallback = f;
	bmp085Control.bmp085isPressureReading = true;
	calibrateBmp085();
	if (BC_FAIL == bmp085PushTask(BMP085_SET_READ_TO_TEMP))
		return BC_FAIL;
	if (BC_FAIL == bmp085PushTask(BMP085_WAIT))
		return BC_FAIL;
	if (BC_FAIL == bmp085PushTask(BMP085_GET_UTEMP))
		return BC_FAIL;
	if (BC_FAIL == bmp085PushTask(BMP085_FORMAT_TEMP_DATA))
		return BC_FAIL;
	if (BC_FAIL == bmp085PushTask(BMP085_SET_READ_TO_PRESSURE))
		return BC_FAIL;
	if (BC_FAIL == bmp085PushTask(BMP085_WAIT))
		return BC_FAIL;
	if (BC_FAIL == bmp085PushTask(BMP085_GET_UPRESSURE))
		return BC_FAIL;
	if (BC_FAIL == bmp085PushTask(BMP085_FORMAT_PRESSURE_DATA))
		return BC_FAIL;
	if (BC_FAIL == bmp085PushTask(BMP085_RETURN))
		return BC_FAIL;
	bspPostTask(BSP_PRESSURE);
	return BC_SUCCESS;
}


/**************************************************************************//**
\brief BMP085 Task Handler.
******************************************************************************/
void bspBMP085Handler(void)
{
	Bmp085States_t task;
	task = bmp085PopTask();

	if (task ==	BMP085_IDLE)
	{	// IDLE state, does nothing
		return;
	}

	if (task ==	BMP085_RETURN)
	{	// if queue is empty, call top layer task handler, else continue to next task
		if (BMP085_IDLE == bmp085State[0])
		{
			if (bmp085Control.bmp085isPressureReading)
				bmp085Control.bmp085PressureCallback(bmp085Control.bmp085Result, bmp085Control.bmp085Pressure);
			else
				bmp085Control.bmp085TempCallback(bmp085Control.bmp085Result, bmp085Control.bmp085Temperature);
		}		
		else
			bspPostTask(BSP_PRESSURE);
		return;
	} // RETURN

	if (task ==	BMP085_WAIT)
	{	// wait BMP085_WAIT milliseconds to jump to the next task
		bmp085Delay.interval = BMP085_DELAY;					// Timer interval in milliseconds
		bmp085Delay.mode     = TIMER_ONE_SHOT_MODE;				// One shot mode (TIMER_REPEAT_MODE or TIMER_ONE_SHOT_MODE)
		bmp085Delay.callback = bspBMP085Handler;				// Callback function for timer fire event
		HAL_StartAppTimer(&bmp085Delay);	
		return;
	} // WAIT

	if (task ==	BMP085_GET_CAL)
	{	// read the calibrations values stored in the PROM
		if (-1 == HAL_OpenI2cPacket(&i2cParam))
		{
			bmp085I2CHandler(false);
			return;
		}			
  
		i2cParam.data = (uint8_t*)(&bmp085Calibration.prom_data);	// where to store the data
		i2cParam.length = BMP085_PROM_DATA_LEN;						// read BMP085_PROM_DATA_LEN bytes
		i2cParam.lengthAddr = HAL_ONE_BYTE_SIZE;					// address has one byte
		i2cParam.internalAddr = BMP085_PROM_START_ADDR;				// start reading from BMP085_PROM_START_ADDR
		i2cParam.f = bmp085I2CHandler;								// when finished call bmp085I2CHandler
		
		if (-1 == HAL_ReadI2cPacket(&i2cParam))
			bmp085I2CHandler(false);

		return;
	} // GET_CAL
	
	if (task == BMP085_FORMAT_CAL_DATA)
	{	/* take the raw data stores in  bmp085Calibration.prom_data and transform it into variables */
		/*parameter ac1, ac2, ac3, ac4, ac5, ac6*/
		bmp085Calibration.ac1 =  (bmp085Calibration.prom_data[0] <<8) | bmp085Calibration.prom_data[1];
		bmp085Calibration.ac2 =  (bmp085Calibration.prom_data[2] <<8) | bmp085Calibration.prom_data[3];
		bmp085Calibration.ac3 =  (bmp085Calibration.prom_data[4] <<8) | bmp085Calibration.prom_data[5];
		bmp085Calibration.ac4 =  (bmp085Calibration.prom_data[6] <<8) | bmp085Calibration.prom_data[7];
		bmp085Calibration.ac5 =  (bmp085Calibration.prom_data[8] <<8) | bmp085Calibration.prom_data[9];
		bmp085Calibration.ac6 = (bmp085Calibration.prom_data[10] <<8) | bmp085Calibration.prom_data[11];
  
		/*parameters B1,B2*/
		bmp085Calibration.b1 =  (bmp085Calibration.prom_data[12] <<8) | bmp085Calibration.prom_data[13];
		bmp085Calibration.b2 =  (bmp085Calibration.prom_data[14] <<8) | bmp085Calibration.prom_data[15];
  
		/*parameters MB,MC,MD*/
		bmp085Calibration.mb =  (bmp085Calibration.prom_data[16] <<8) | bmp085Calibration.prom_data[17];
		bmp085Calibration.mc =  (bmp085Calibration.prom_data[18] <<8) | bmp085Calibration.prom_data[19];
		bmp085Calibration.md =  (bmp085Calibration.prom_data[20] <<8) | bmp085Calibration.prom_data[21];
		
		bspPostTask(BSP_PRESSURE);
		return;
	} // FORMAT_CAL_DATA
	
	if (task ==	BMP085_SET_READ_TO_TEMP)
	{	// Set configuration register for temperature reading
		if (-1 == HAL_OpenI2cPacket(&i2cParam))
		{
			bmp085I2CHandler(false);
			return;
		}
  
		ctrl_reg_data = BMP085_T_MEASURE;					// Write 0x24
		i2cParam.data = &ctrl_reg_data;						// to configuration register 0xF4
		i2cParam.length = 1;
		i2cParam.lengthAddr = HAL_ONE_BYTE_SIZE;
		i2cParam.internalAddr = BMP085_CTRL_MEAS_REGISTER;
		i2cParam.f = bmp085I2CHandler;

		if (-1 == HAL_WriteI2cPacket(&i2cParam))
			bmp085I2CHandler(false);

		return;
	} // GET_TEMP
	

	if (task ==	BMP085_GET_UTEMP)
	{	// Read uncalibrated temperature
		if (-1 == HAL_OpenI2cPacket(&i2cParam))
		{
			bmp085I2CHandler(false);
			return;
		}			
  
		i2cParam.data = (uint8_t*)(&bmp085Calibration.ut);
		i2cParam.length = 2;									// 16bit word
		i2cParam.lengthAddr = HAL_ONE_BYTE_SIZE;
		i2cParam.internalAddr = BMP085_ADC_OUT_MSB_REGISTER;
		i2cParam.f = bmp085I2CHandler;

		if (-1 == HAL_ReadI2cPacket(&i2cParam))
			bmp085I2CHandler(false);

		return;		
	} // GET_UTEMP		

	if (task ==	BMP085_FORMAT_TEMP_DATA)
	{	// Calculate calibrated temperature and param_b5 for forward pressure calculation
		ut = (bmp085Calibration.ut[0] <<8) | bmp085Calibration.ut[1];
		x1 = ((int32_t)((uint16_t) ut - bmp085Calibration.ac6) * (int32_t) bmp085Calibration.ac5) >> 15;
		x2 = ((int32_t) bmp085Calibration.mc << 11) / (x1 + bmp085Calibration.md);
		bmp085Calibration.param_b5 = x1 + x2;
		bmp085Control.bmp085Temperature = ((bmp085Calibration.param_b5 + 8) >> 4);

		bspPostTask(BSP_PRESSURE);
		return;
	} // FORMAT_TEMP_DATA

	if (task == BMP085_SET_READ_TO_PRESSURE)
	{	// Set configuration register for pressure reading
		if (-1 == HAL_OpenI2cPacket(&i2cParam))
		{
			bmp085I2CHandler(false);
			return;
		}			
  
		ctrl_reg_data = BMP085_P_MEASURE + ((uint8_t)BMP085_OVERSAMPLING << 6);	// the value written depends on the oversampling set
		i2cParam.data = &ctrl_reg_data;
		i2cParam.length = 1;
		i2cParam.lengthAddr = HAL_ONE_BYTE_SIZE;
		i2cParam.internalAddr = BMP085_CTRL_MEAS_REGISTER;
		i2cParam.f = bmp085I2CHandler;

		if (-1 == HAL_WriteI2cPacket(&i2cParam))
			bmp085I2CHandler(false);

		return;
	} // GET_PRESSURE

	if (task == BMP085_GET_UPRESSURE)
	{	// Read uncalibrated pressure
		if (-1 == HAL_OpenI2cPacket(&i2cParam))
		{
			bmp085I2CHandler(false);
			return;
		}			
  
		i2cParam.data = (uint8_t*)(&bmp085Calibration.up);
		i2cParam.length = 3;
		i2cParam.lengthAddr = HAL_ONE_BYTE_SIZE;
		i2cParam.internalAddr = BMP085_ADC_OUT_MSB_REGISTER;
		i2cParam.f = bmp085I2CHandler;

		if (-1 == HAL_ReadI2cPacket(&i2cParam))
			bmp085I2CHandler(false);

		return;		
	} // GET_UPRESSURE

	if (task ==	BMP085_FORMAT_PRESSURE_DATA)
	{	// Calculate calibrate pressure
		up = (((uint32_t)bmp085Calibration.up[0] << 16) \
				| ((uint32_t)bmp085Calibration.up[1] << 8) \
				| (uint32_t)bmp085Calibration.up[2]) >> (8-BMP085_OVERSAMPLING);

		
		b6 = bmp085Calibration.param_b5 - 4000;
		//*****calculate B3************
		x1 = (b6*b6) >> 12;
		x1 *= bmp085Calibration.b2;
		x1 >>=11;

		x2 = (bmp085Calibration.ac2*b6);
		x2 >>=11;

		x3 = x1 +x2;

		b3 = (((((int32_t)bmp085Calibration.ac1 )*4 + x3) << BMP085_OVERSAMPLING) + 2) >> 2;

		//*****calculate B4************
		x1 = (bmp085Calibration.ac3* b6) >> 13;
		x2 = (bmp085Calibration.b1 * ((b6*b6) >> 12) ) >> 16;
		x3 = ((x1 + x2) + 2) >> 2;
		b4 = (bmp085Calibration.ac4 * (uint32_t) (x3 + 32768)) >> 15;
     
		b7 = ((uint32_t)(up - b3) * (50000>>BMP085_OVERSAMPLING));
		if (b7 < 0x80000000)
		{
			bmp085Control.bmp085Pressure = (b7 << 1) / b4;
		}
		else
		{
			bmp085Control.bmp085Pressure = (b7 / b4) << 1;
		}

		x1 = bmp085Control.bmp085Pressure >> 8;
		x1 *= x1;
		x1 = (x1 * 3038) >> 16;
		x2 = (-7357 * (bmp085Control.bmp085Pressure)) >> 16;
		bmp085Control.bmp085Pressure += (x1 + x2 + 3791) >> 4;	// pressure in Pa
		
		bspPostTask(BSP_PRESSURE);
		return;
	} // FORMAT_PRESSURE_DATA
}	


// eof tmp175.c
